package net.gz01.wallet.common;

import com.alibaba.fastjson.JSON;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

public class WalletMD5 {

	private final static String[] STRING_DIGITAL = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d",
			"e", "f" };

	public WalletMD5() {
	}

	// 返回形式为数字跟字符串
	private static String byteToArrayString(byte bByte) {
		int iRet = bByte;
		if (iRet < 0) {
			iRet += 256;
		}
		int iD1 = iRet / 16;
		int iD2 = iRet % 16;
		return STRING_DIGITAL[iD1] + STRING_DIGITAL[iD2];
	}

	// 返回形式只为数字
	@SuppressWarnings("unused")
	private static String byteToNum(byte bByte) {
		int iRet = bByte;
		System.out.println("iRet1=" + iRet);
		if (iRet < 0) {
			iRet += 256;
		}
		return String.valueOf(iRet);
	}

	// 转换字节数组为16进制字串
	private static String byteToString(byte[] bByte) {
		StringBuffer sBuffer = new StringBuffer();
		for (int i = 0; i < bByte.length; i++) {
			sBuffer.append(byteToArrayString(bByte[i]));
		}
		return sBuffer.toString();
	}

	/**
	 * UTF8编码
	 *
	 * @param source
	 * @return
	 */
	public static String codeFor(String source) {
		return codeFor(source, "UTF-8");
	}

	/**
	 * 进行加密
	 *
	 * @param source
	 *            加密字符串
	 * @return
	 */
	public static String codeFor(String source, String charset) {
		String resultString = null;
		try {
			resultString = new String(source);
			MessageDigest md = MessageDigest.getInstance("MD5");
			// md.digest() 该函数返回值为存放哈希值结果的byte数组
			resultString = byteToString(md.digest(source.getBytes(charset)));
		} catch (NoSuchAlgorithmException ex) {
			ex.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return resultString;
	}

	/**
	 * 将有序的map按照key的ascii码排序，组合成key1=valu1&key2=valu2...&keyN=valueN，再进行md5运算，返回结果的ascii编码大写
	 * @param map 有序的，value是null值，空字符串会自动舍去
	 * param saleKey
	 * @return
	 */
	public static String sign(SortedMap<String, String> map, String safeKey) {
		StringBuilder buff = new StringBuilder();
		map.forEach((key, value)->{
			if (value != null && !value.isEmpty()) {
				buff.append(key + "=" + value + "&");
			}
		});
		buff.append(safeKey);
		return codeFor(buff.toString(), "UTF-8").toUpperCase();
	}

	/**
	 * 字典排序加密
	 *
	 * @param object
	 *            参与签名对象，目前只支持Map,String
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static String sortSign(Object object, String safeKey, String... skipKey) {
		TreeMap<String, String> signMap = new TreeMap<String, String>();
		String json = null;
		if (object instanceof String){
			json = (String)object;
		}else {
			json = JSON.toJSONString(object);
		}
		Map<String, String> tmpMap = JSON.parseObject(json, Map.class);
		Set<String> keys = tmpMap.keySet();
		// 遍历jsonObject数据，添加到Map对象
		for (String key : keys) {
			String value = tmpMap.get(key);
			signMap.put(key, value);
		}

		StringBuffer encryptData = new StringBuffer();
		// 剔除不需要参与前面的字段
		for (String skip : skipKey) {
			signMap.remove(skip);
		}
		signMap.remove("sign");
		for (Entry<String, String> entry : signMap.entrySet()) {
			String key = entry.getKey();
			String value = entry.getValue();
			if (value != null && !value.isEmpty() && !"null".equals(value)) {
				encryptData.append(key + "=" + value + "&");
			}
		}
		if (safeKey != null) {
			encryptData.append(safeKey);
		}
		System.out.println("参与签名字符串为 -> " + hide(encryptData.toString(), "cardNo", "cvn2", "expired"));
		return codeFor(encryptData.toString(), "UTF-8").toUpperCase();
	}

	/**
	 * 使用 Map按key进行排序
	 *
	 * @param map
	 * @return
	 */
	public static Map<String, Object> sortMapByKey(Map<String, String> map) {
		if (map == null || map.isEmpty()) {
			return null;
		}
		Map<String, Object> sortMap = new TreeMap<String, Object>((str1, str2) -> str1.compareTo(str2));
		sortMap.putAll(map);
		return sortMap;
	}

	/**
	 * 隐藏敏感字段的值
	 *
	 * @param src
	 * @param keywords
	 * @return
	 */
	private static String hide(String src, String... keywords) {
		char[] c = src.toCharArray();
		for (int i = 0; i < keywords.length; i++) {
			String keyword = keywords[i] + "=";
			int start = src.indexOf(keyword);
			if (start < 0) {
				continue;
			}

			int end = src.indexOf("&", start + keyword.length());
			int length = end - start - keyword.length();
			for (int k = start + keyword.length(); k < end; k++) {
				if (length > 10) {
					if (k > start + keyword.length() + 5 && k < end - 4) {
						c[k] = '*';
					}
				} else {
					c[k] = '*';
				}
			}
		}
		return String.valueOf(c);
	}
}
